package com.baturu.vin.benz.service.impl;

import com.baturu.vin.benz.dal.dao.BenzFootnoteDAO;
import com.baturu.vin.benz.dal.dao.BenzRulesDAO;
import com.baturu.vin.benz.dto.*;
import com.baturu.vin.benz.dto.footnote.FootnoteList;
import com.baturu.vin.benz.dto.footnote.PartInfo;
import com.baturu.vin.benz.service.impl.util.BenzStringUtil;
import com.baturu.vin.benz.service.impl.util.BenzValidateCodeBUtil;
import com.baturu.vin.kit.StreamKit;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.OptionalInt;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 奔驰vin码和配件编码解析器
 *
 * @author 蒋鑫磊, majanming
 * @Time 2016/11/28.
 */
@Log4j2
@Service("benzFilterService")
public class BenzFilterService {

    @Autowired
    private BenzCarTypeInfoService benzCarTypeInfoService;
    @Autowired
    private BenzFootnotesService benzFootnotesService;
    @Autowired
    private BenzSpringService benzSpringService;
    @Autowired
    private BenzRulesDAO benzRulesDAO;
    @Autowired
    private BenzFootnoteDAO benzFootnoteDAO;


    private static Set<String> filterGroupNum = Sets.newHashSet("98", "99");

    public List<BenzEPCPartsInfoDTO> filterByRules(List<BenzEPCPartsInfoDTO> partsInfos, BenzVinInfoDTO benzVinInfoDTO) {
        //将所有配件码的替换编码全都加入映射关系
        fillReplacePart(partsInfos);
        //针对多个配件的,底盘过滤
        filterByCarModelNum(partsInfos, benzVinInfoDTO.getCatInfoMap());
        //过滤左右肽的配件
        filterSteerTrans(partsInfos, benzVinInfoDTO);
        //过滤98,99组的配件
        filterNoUseGroupNum(partsInfos);
        // 如果有多个配件编码,取他们的SA代码进行过滤
        filterBySaCode(partsInfos, benzVinInfoDTO.getSaCodeSet(), benzVinInfoDTO.getCatInfoMapByCatNum());
        //SA过滤
        //用车型的脚注
        filterPartsByFootnotes(partsInfos, benzVinInfoDTO);
        //弹簧过滤
        filterPartsBySprings(partsInfos, benzVinInfoDTO);
        //扩展的代码分析
        filterCodeEvaluation(partsInfos, benzVinInfoDTO.getCatInfoMapByCatNum(), benzVinInfoDTO.getSaCodeSet());
        // 填充色码
        fillColor(partsInfos);
        //过滤多个编码(存在替换关系)
        partsInfos = filterReplacePart(partsInfos);
        return partsInfos;
    }


    private void filterNoUseGroupNum(List<BenzEPCPartsInfoDTO> partsInfo) {
        if (CollectionUtils.isNotEmpty(partsInfo)) {
            partsInfo.removeIf(p -> filterGroupNum.contains(p.getGroupNum()));
        }
    }

    private List<BenzEPCPartsInfoDTO> filterReplacePart(List<BenzEPCPartsInfoDTO> partsInfo) {
      /*
        if (partsInfo.size() > 1) {
            List<BenzEPCPartsInfoDTO> notReplaceParts = partsInfo.stream().filter(p -> !Objects.equals(p.getReplFlag(), "R")).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(notReplaceParts)) {
                partsInfo.removeIf(p -> Objects.equals(p.getReplFlag(), "R"));
            } else {
                Set<String> partsCodes = partsInfo.stream().map(BenzEPCPartsInfoDTO::getCbPartsCode).collect(Collectors.toSet());
                partsInfo.removeIf(p -> partsCodes.contains(StringUtils.trim(p.getReptype()) + StringUtils.trim(p.getReppno())));
            }
        }
       */
        if (CollectionUtils.isEmpty(partsInfo)) {
            return Lists.newArrayList();
        }
        if (partsInfo.size() == 1) {
            return partsInfo;
        }
        Set<String> allPartsCodes = partsInfo.stream().map(BenzEPCPartsInfoDTO::getCbPartsCode).collect(Collectors.toSet());

        return partsInfo.stream().filter(p -> !StringUtils.equals("R", p.getReplFlag()) || !allPartsCodes.contains(StringUtils.trim(p.getReptype()) + StringUtils.trim(p.getReppno()))).collect(Collectors.toList());
    }

    private void fillColor(List<BenzEPCPartsInfoDTO> partsInfo) {
        if (CollectionUtils.isNotEmpty(partsInfo)) {
            partsInfo.forEach(p -> {
                if (p.getPartNum().trim().length() == 14) {
                    return;
                }
                if (!"9999".equals(p.getEs2Code()) && StringUtils.isNotBlank(p.getEs2Code())) {
                    p.setPartNum(p.getPartNum().trim() + p.getEs2Code());
                }
            });
        }

        //可能有同一个编码一个带颜色一个不带颜色的情况,所以加一次过滤
        if (CollectionUtils.isNotEmpty(partsInfo)) {
            List<Integer> filterIds = Lists.newArrayList();
            Map<String, List<BenzEPCPartsInfoDTO>> map = partsInfo.stream().collect(Collectors.groupingBy(BenzEPCPartsInfoDTO::getCbPartsCode));
            map.keySet().forEach(key -> {
                List<BenzEPCPartsInfoDTO> partsInfoDTOS = map.get(key);
                List<BenzEPCPartsInfoDTO> filterParts = partsInfoDTOS.stream().filter(p -> p.getCbPartsCode().equals(p.getPartsType().trim() + p.getPartNum().trim())).collect(Collectors.toList());
                if (filterParts.size() > 0 && filterParts.size() != partsInfoDTOS.size()) {
                    filterParts.forEach(p -> filterIds.add(p.getId()));
                }
            });
            if (CollectionUtils.isNotEmpty(filterIds)) {
                partsInfo.removeIf(p -> filterIds.contains(p.getId()));
            }
        }

    }

    private void filterPartsBySprings(List<BenzEPCPartsInfoDTO> partsInfos, BenzVinInfoDTO benzVinInfoDTO) {
        BenzSpring benzSpring = benzCarTypeInfoService.getBenzSpring(benzVinInfoDTO.getWhc(), benzVinInfoDTO.getChassbm(), benzVinInfoDTO.getChassIdent());
        if (benzSpring == null) {
            return;
        }
        if (CollectionUtils.isNotEmpty(partsInfos)) {
            List<BenzEPCPartsInfoDTO> springPartsDTOs = partsInfos.stream().filter(p -> StringUtils.equals(p.getGroupNum(), "32")).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(springPartsDTOs)) {
                return;
            }
            springPartsDTOs.removeAll(benzSpringService.validateSpring(springPartsDTOs, benzSpring));
            partsInfos.removeAll(springPartsDTOs);
        }
    }

    private void filterCodeEvaluation(List<BenzEPCPartsInfoDTO> partsInfos, Map<String, BenzPartCatInfoDTO> catInfoMapByCatNum, Set<String> saCodeSet) {
        if (CollectionUtils.isNotEmpty(partsInfos) && BenzValidateCodeBUtil.extendCodeFilter(partsInfos, catInfoMapByCatNum)) {
            List<BenzEPCPartsInfoDTO> partsToRemove = Lists.newArrayList();
            Map<String, List<BenzEPCPartsInfoDTO>> partsByCalloutPosAddrHash = BenzValidateCodeBUtil.getPartsByCallout(partsInfos);
            if (partsByCalloutPosAddrHash.isEmpty()) {
                return;
            }
            Set<String> e = partsByCalloutPosAddrHash.keySet();
            for (String calloutPosAddr : e) {
                List<BenzEPCPartsInfoDTO> partsWithSameCalloutPosAddr = partsByCalloutPosAddrHash.get(calloutPosAddr);
                int highScore = 0;
                if (CollectionUtils.isEmpty(partsWithSameCalloutPosAddr)) {
                    continue;
                }
                for (BenzEPCPartsInfoDTO p : partsWithSameCalloutPosAddr) {
                    if (StringUtils.isBlank(p.getSaCode()) || p.getSaCode().trim().length() == 0) {
                        continue;
                    }
                    int score = BenzStringUtil.scoreCodeBString(p.getSaCode(), saCodeSet);
                    p.setCodeBScore(score);
                    if (score > highScore) {
                        highScore = score;
                    }
                }
                for (BenzEPCPartsInfoDTO p : partsWithSameCalloutPosAddr) {
                    if (p.getCodeBScore() < highScore) {
                        partsToRemove.add(p);
                    }
                }
            }
            partsInfos.removeAll(partsToRemove);
        }
    }

    private void filterBySaCode(List<BenzEPCPartsInfoDTO> partsInfos, Set<String> saCodeSet, Map<String, BenzPartCatInfoDTO> catInfoMapByCatNum) {
        if (CollectionUtils.isEmpty(partsInfos)) {
            return;
        }
        setAccessoryPart(partsInfos, catInfoMapByCatNum);
        partsInfos.removeIf(e -> !BenzStringUtil.filterBySaCode(e, saCodeSet, catInfoMapByCatNum));
    }

    private void setAccessoryPart(List<BenzEPCPartsInfoDTO> partList, Map<String, BenzPartCatInfoDTO> catInfoMapByCatNum) {
        partList.stream()
                .filter(e -> StringUtils.isNotBlank(e.getSaCode()))
                .forEach(e -> e.setAccessoryPart(BenzStringUtil.isAccessoryRecord(e.getSaCode(), catInfoMapByCatNum.get(e.getCatNum()).getSource())));
    }

    private void filterPartsByFootnotes(List<BenzEPCPartsInfoDTO> partsInfos, BenzVinInfoDTO benzVinInfoDTO) {
        if (CollectionUtils.isEmpty(partsInfos)) {
            return;
        }

        List<BenzFootnoteDTO> benzFootnoteDTOs = benzCarTypeInfoService.findFootnotesByParts(partsInfos);

        if (CollectionUtils.isEmpty(benzFootnoteDTOs)) {
            return;
        }
        Map<String, List<BenzFootnoteDTO>> benzFootnoteDTOsGroupBy = benzFootnoteDTOs.stream().collect(Collectors.groupingBy(b -> StringUtils.upperCase(b.getCatNum() + b.getGroupNum())));

        List<Map<String, String>> plantsCodesMap = benzFootnoteDAO.getPlantsCodes();
        Map<String, String> plantsCodes = Maps.newHashMap(
                plantsCodesMap.stream()
                        .collect(Collectors.toMap(k -> k.get("whc"), k -> k.get("plants"), (k1, k2) -> k1))
        );
        Map<String, BenzPartCatInfoDTO> catInfoMapByCatNum = benzVinInfoDTO.getCatInfoMapByCatNum();
        String whc = benzVinInfoDTO.getWhc();
        String plants = plantsCodes.get(whc);
        Map<String, PartInfo> catGrpFns = benzFootnoteDTOsGroupBy.values().stream().collect(Collectors.toMap(
                list -> {
                    BenzFootnoteDTO benzFootnoteDTO = list.get(0);
                    String catNum = benzFootnoteDTO.getCatNum();
                    String groupNum = benzFootnoteDTO.getGroupNum();
                    return catNum + "_" + groupNum;
                }, list -> {
                    BenzFootnoteDTO b = list.get(0);
                    String catNum = b.getCatNum();
                    String source = catInfoMapByCatNum.get(catNum).getSource();
                    FootnoteList fn = benzFootnotesService.getFootnoteList(source, plants, list);
                    FootnoteList filteredFootNotes = benzFootnotesService.filterFootNoteList(fn, fn, benzVinInfoDTO, benzVinInfoDTO.getCatInfoMapByCatNum().get(catNum));
                    return new PartInfo(fn, filteredFootNotes);
                },
                (l1, l2) -> l1
        ));
        partsInfos.removeIf(p -> !benzFootnotesService.validatePartByFootnote(p, benzVinInfoDTO, catGrpFns));
    }

    private void filterSteerTrans(List<BenzEPCPartsInfoDTO> partsInfos, BenzVinInfoDTO benzVinInfoDTO) {
        if (CollectionUtils.isNotEmpty(partsInfos)) {
            partsInfos.removeIf(p -> !BenzStringUtil.filterBySteering(p, benzVinInfoDTO));
        }
    }

    private void filterByCarModelNum
            (List<BenzEPCPartsInfoDTO> partsInfos, Map<String, BenzPartCatInfoDTO> catInfoMap) {
        if (CollectionUtils.isNotEmpty(partsInfos)) {
            partsInfos.removeIf(p -> !checkMatchCarInfo(p, catInfoMap));
        }
    }

    private boolean checkMatchCarInfo(BenzEPCPartsInfoDTO
                                              partsInfoDTO, Map<String, BenzPartCatInfoDTO> catInfoMap) {
        String partMdl = partsInfoDTO.getType();
        String partSubMod = partsInfoDTO.getSubmods();
        if (StringUtils.isBlank(partSubMod)) {
            return true;
        }
        return catInfoMap.values().stream().anyMatch((catInfoDTO) -> {
            if (catInfoDTO == null) {
                return true;
            }
            String catType2 = catInfoDTO.getCatTyp2();
            if (!partMdl.trim().equals(catInfoDTO.getType())) {
                return false;
            }
            String subbm;
            if (catType2 != null && "9".equals(catType2.trim())) {
                subbm = catInfoDTO.getSubbm2();
            } else {
                subbm = catInfoDTO.getSubbm1();
            }
            for (int i = 0; i < partSubMod.length(); i += 3) {
                if (subbm != null && subbm.equals(partSubMod.substring(i, i + 3))) {
                    return true;
                }
            }
            return false;
        });
    }

    private void fillReplacePart(List<BenzEPCPartsInfoDTO> partsInfos) {
        if (CollectionUtils.isNotEmpty(partsInfos)) {
            Set<BenzEPCPartsInfoDTO> repInfo = partsInfos.stream().filter(p ->
                    StringUtils.isNotBlank(p.getReplFlag()) && StringUtils.isNotBlank(p.getReptype()) && StringUtils.isNotBlank(p.getReppno())
            ).map(p -> BenzEPCPartsInfoDTO.builder().catNum(p.getCatNum())
                    .groupNum(p.getGroupNum()).subGroup(p.getSubGroup()).callout(p.getCallout())
                    .cbPartsCode(p.getPartsType() + p.getReppno().replaceAll(" ", ""))
                    .build()
            ).collect(Collectors.toSet());
            if (CollectionUtils.isEmpty(repInfo)) {
                return;
            }

            List<BenzEPCPartsInfoDTO> partsByRepInfo = benzCarTypeInfoService.findPartsByRepInfo(repInfo);
            if (CollectionUtils.isEmpty(partsByRepInfo)) {
                return;
            }

            Map<String, List<BenzEPCPartsInfoDTO>> mapByPartsCode = partsByRepInfo.stream().collect(Collectors.groupingBy(BenzEPCPartsInfoDTO::getCbPartsCode));

            for (BenzEPCPartsInfoDTO p : partsInfos) {
                if (StringUtils.isNotBlank(p.getReplFlag()) && StringUtils.isNotBlank(p.getReptype()) && StringUtils.isNotBlank(p.getReppno())) {
                    String partsCode = p.getPartsType() + p.getReppno().replaceAll(" ", "");
                    List<BenzEPCPartsInfoDTO> benzEPCPartsInfoDTOs = mapByPartsCode.get(partsCode);
                    if (CollectionUtils.isEmpty(benzEPCPartsInfoDTOs)) {
                        continue;
                    }
                    if (StringUtils.isNotBlank(p.getBtrPartsName())) {
                        for (BenzEPCPartsInfoDTO benzEPCPartsInfoDTO : benzEPCPartsInfoDTOs) {
                            if (p.getCatNum().equalsIgnoreCase(benzEPCPartsInfoDTO.getCatNum()) &&
                                    p.getGroupNum().equalsIgnoreCase(benzEPCPartsInfoDTO.getGroupNum()) &&
                                    p.getSubGroup().equalsIgnoreCase(benzEPCPartsInfoDTO.getSubGroup()) &&
                                    p.getCallout().equalsIgnoreCase(benzEPCPartsInfoDTO.getCallout())) {
                                benzEPCPartsInfoDTO.setBtrPartsName(p.getBtrPartsName());
                            }
                        }
                    }
                }
            }
            partsInfos.addAll(partsByRepInfo);
        }
    }

    public Map<String, List<BenzEPCPartsInfoDTO>> filterGroupByPartsName
            (Map<String, List<BenzEPCPartsInfoDTO>> partsInfoGroupByPartsName, BenzVinInfoDTO analyseResult) {

        Map<String, List<BenzRulesDTO>> benzRulesDTOMap = Maps.newHashMap();
        Set<String> partsNames = partsInfoGroupByPartsName.keySet();
        if (CollectionUtils.isNotEmpty(partsNames)) {
            benzRulesDTOMap.putAll(StreamKit.partitionStream(partsNames, 150).flatMap(l ->
                    benzRulesDAO.queryRules(l).stream()
            ).collect(Collectors.groupingBy(BenzRulesDTO::getBtrPartsName)));
        }

        partsInfoGroupByPartsName.entrySet().forEach(v -> {

            List<BenzRulesDTO> rules = benzRulesDTOMap.get(v.getKey());
            if (CollectionUtils.isNotEmpty(rules)) {
                List<BenzEPCPartsInfoDTO> partsInfosBackup = Lists.newArrayList(v.getValue());
                rules.forEach(r ->
                        partsInfosBackup.removeIf(p -> !include(p, r))
                );
                if (CollectionUtils.isNotEmpty(partsInfosBackup)) {
                    v.getValue().clear();
                    v.getValue().addAll(partsInfosBackup);
                }
            }
            if (CollectionUtils.size(v.getValue()) > 1) {
                List<BenzEPCPartsInfoDTO> partsInfosBackup = Lists.newArrayList(v.getValue());
                filterOptPart(partsInfosBackup);
                List<String> partsCode = partsInfosBackup.stream().map(BenzEPCPartsInfoDTO::getPartsCode).distinct().collect(Collectors.toList());
                if (CollectionUtils.size(partsCode) == 1) {
                    v.getValue().clear();
                    v.getValue().addAll(partsInfosBackup);
                }
            }
        });
        return partsInfoGroupByPartsName;
    }

    private boolean include(BenzEPCPartsInfoDTO p, BenzRulesDTO rule) {
        List<String> rules = Splitter.on(",").splitToList(rule.getRule());
        boolean rBool;
        for (String r : rules) {
            List<String> rList = Splitter.on(":").splitToList(r);
            String name = rList.get(0);
            String include = rList.get(1);
            Class<? extends BenzEPCPartsInfoDTO> clazz = p.getClass();
            try {
                Field field = clazz.getDeclaredField(name);
                field.setAccessible(true);
                String s = (String) field.get(p);
                rBool = StringUtils.contains(s, include);
                if (!rBool) {
                    return false;
                }
            } catch (Exception e) {
                log.warn("benz配件字段有误，请核查partName:{},excludeFieldName:{}", p.getBtrPartsName(), name);
            }
        }
        return true;
    }

    private void filterOptPart(List<BenzEPCPartsInfoDTO> partsInfos) {
        List<BenzEPCPartsInfoDTO> opParts = partsInfos.stream()
                .filter(d -> StringUtils.equals("W", d.getOptflag()))
                .collect(Collectors.toList());

        if (CollectionUtils.isNotEmpty(opParts)) {
            OptionalInt max = opParts.stream().mapToInt(BenzEPCPartsInfoDTO::getId).max();
            partsInfos.removeAll(opParts);
            opParts = opParts.stream().filter(d -> d.getId() == max.getAsInt()).collect(Collectors.toList());
            partsInfos.addAll(opParts);
        }

    }
}
